//
//	Implementation of the sequence document object
//
#include "sequence.h"

//MUST be a multiple of the OS allocation granularity
//(e.g. this will normally be 64Kb)
#define MEM_BLOCK_SIZE 0x10000*2	//actually, this is 128Kb	

BOOL sequence::init(char *filename)
{
	HANDLE hTemp;
	
	hTemp = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
	
	//if we failed to open the file, then exit now and keep
	//the current file open
	if(hTemp == INVALID_HANDLE_VALUE)
		return FALSE;

	//now close the file
	close();

	hFile = hTemp;

	//create a file mapping which spans the entire file.
	//This works even for the 4Gb file sizes
	hMemMap = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);

	length = GetFileSize(hFile, 0);
	filebufferlength = length;

	return TRUE;
}

BOOL sequence::close()
{
	if(filebuffer == 0)
		return FALSE;

	//close the view of the file
	UnmapViewOfFile(filebuffer);

	//
	CloseHandle(hMemMap);
	CloseHandle(hFile);

	hMemMap = 0;
	hFile   = 0;
	
	//clear both buffer pointers
	filebuffer   = 0;
	adjustedptr  = 0;

	mappedoffset = 0;
	mappedlength = 0;
	filebufferlength = 0;
	length = 0;

	return TRUE;
}

//
//	Return the logical size of the sequence. 
//
size_w sequence::size()
{
	return length;
}

/*
                  :       :
 0 --- 32 --------:--96 --:------------ 160 ---------------
       |        a : b | c : d            |
 ---------- 64 ---:-------:- 128 ------------------192----
 |           |    :       :    |                     |
 -----------------:-------:--------------------------------
             ^    :       :    ^
             ^
  a - evaluates to 32
  b - evaluates to 64
  c - evaluates to 64
  d - evaluates to 96
*/
size_w inline calc_index_base(size_w index)
{
	if(index < MEM_BLOCK_SIZE / 2)
	{
		return 0;
	}
	else
	{
		return ((index + MEM_BLOCK_SIZE / 4) & (~(MEM_BLOCK_SIZE / 2 - 1))) - MEM_BLOCK_SIZE / 2;
	}
}


BYTE *sequence::get_adjusted_ptr(size_w offset)
{
	size_w baseoff = calc_index_base(offset);

	//if we already have the right range of memory mapped in
	if(adjustedptr != 0 && mappedoffset == baseoff)
	{
		return adjustedptr;
	}
	//otherwise, map in the new area
	else
	{
		if(filebuffer)
			UnmapViewOfFile(filebuffer);

		mappedlength = min(filebufferlength - baseoff, MEM_BLOCK_SIZE);

		filebuffer = (BYTE *)MapViewOfFile(hMemMap, FILE_MAP_READ, 0, baseoff, mappedlength);

		mappedoffset = baseoff;
		adjustedptr = filebuffer - mappedoffset;	

		return adjustedptr;
	}
}

BOOL sequence::render(BYTE *buffer, size_w offset, size_w length)
{
	// If we had the entire file mapped into memory at once, then
	// we could do a simple memcpy to render the data in one go.
	//
	// However, we have to do the copies in sections. When
	// we get a pointer to the file with get_adjusted_ptr, this
	// pointer only covers a range -MEM_BLOCK_SIZE/4 to +MEM_BLOCK_SIZE/4,
	// so we have to break the copies up into smaller units.
	//
	while(length != 0)
	{
		size_w len = min(length, MEM_BLOCK_SIZE / 4);

		BYTE *source = get_adjusted_ptr(offset);

		if(source == 0)
			return FALSE;

		memcpy(buffer, source + offset, len);

		offset += len;	//advance the source position
		buffer += len;	//advance the destination offset
		length -= len;	//decrement the length of the copy
	}

	return TRUE;
}
